﻿using System;
using System.Xml;
using System.Reflection;
using Microsoft.Xna.Framework;

namespace ThreeDContentPipeline
{
    public class XmlImporter : ContentImporter<object>
    {
        public override object Import(string file, ContentImporterContext context)
        {
            XmlDocument document = new XmlDocument();

            document.Load(file);

            return CreateObjectContent(document.DocumentElement, FindType(document.DocumentElement.GetAttribute("Type")));
        }

        private object CreateObjectContent(XmlElement root, Type type)
        {
            object objContent;

            if (type.IsArray)
                return CreateArrayContent(root, type.GetElementType());

            objContent = Activator.CreateInstance(type);

            foreach (XmlNode node in root.ChildNodes)
            {
                XmlElement element = node as XmlElement;

                if (element != null)
                {
                    FieldInfo fieldInfo = type.GetField(element.Name);

                    if (fieldInfo != null)
                    {
                        fieldInfo.SetValue(objContent, CreateObject(element, fieldInfo.FieldType));
                    }
                    else
                    {
                        PropertyInfo propertyInfo = type.GetProperty(element.Name);

                        propertyInfo.SetValue(objContent, CreateObject(element, propertyInfo.PropertyType), null);
                    }
                }
            }
            return objContent;
        }

        private object CreateObject(XmlElement element, Type type)
        {
            if (type == typeof(short))
            {
                return short.Parse(element.InnerText);
            }
            else if (type == typeof(int))
            {
                return int.Parse(element.InnerText);
            }
            else if (type == typeof(float))
            {
                return float.Parse(element.InnerText);
            }
            else if (type == typeof(string))
            {
                return element.InnerText;
            }
            else if (type == typeof(bool))
            {
                return bool.Parse(element.InnerText);
            }
            else if (type == typeof(Vector2))
            {
                return ParserHelper.ParseVector2(element.InnerText);
            }
            else if (type == typeof(Vector3))
            {
                return ParserHelper.ParseVector3(element.InnerText);
            }
            else if (type == typeof(Vector4))
            {
                return ParserHelper.ParseVector4(element.InnerText);
            }
            else if (type.IsEnum)
            {
                return Enum.Parse(type, element.InnerText, true);
            }
            else if (type == typeof(ExternalReference))
            {
                return new ExternalReference(element.InnerText);
            }
            else
            {
                if (element.HasAttribute("Type"))
                {
                    type = FindType(element.GetAttribute("Type"));
                }
                return CreateObjectContent(element, type);
            }
        }

        private object CreateArrayContent(XmlElement root, Type elementType)
        {
            Array arrayContent;

            if (elementType == typeof(short))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length);
                for (int i = 0; i != tokens.Length; i++)
                {
                    arrayContent.SetValue(short.Parse(tokens[i]), i);
                }
            }
            else if (elementType == typeof(int))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length);
                for (int i = 0; i != tokens.Length; i++)
                {
                    arrayContent.SetValue(int.Parse(tokens[i]), i);
                }
            }
            else if (elementType == typeof(float))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length);
                for (int i = 0; i != tokens.Length; i++)
                {
                    arrayContent.SetValue(float.Parse(tokens[i]), i);
                }
            }
            else if (elementType == typeof(Vector2))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length / 2);
                for (int i = 0, j = 0; i != tokens.Length; j++)
                {
                    arrayContent.SetValue(new Vector2(float.Parse(tokens[i++]), float.Parse(tokens[i++])), j);
                }
            }
            else if (elementType == typeof(Vector3))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length / 3);
                for (int i = 0, j = 0; i != tokens.Length; j++)
                {
                    arrayContent.SetValue(new Vector3(float.Parse(tokens[i++]), float.Parse(tokens[i++]), float.Parse(tokens[i++])), j);
                }
            }
            else if (elementType == typeof(Vector4))
            {
                string[] tokens = root.InnerText.Split(new char[] { ' ', '\t', '\r', '\n', ',' }, StringSplitOptions.RemoveEmptyEntries);

                arrayContent = Array.CreateInstance(elementType, tokens.Length / 4);
                for (int i = 0, j = 0; i != tokens.Length; j++)
                {
                    arrayContent.SetValue(new Vector4(float.Parse(tokens[i++]), float.Parse(tokens[i++]), float.Parse(tokens[i++]), float.Parse(tokens[i++])), j);
                }
            }
            else
            {
                arrayContent = Array.CreateInstance(elementType, root.ChildNodes.Count);

                for (int i = 0; i != root.ChildNodes.Count; i++)
                {
                    XmlElement element = root.ChildNodes[i] as XmlElement;

                    if (element != null)
                    {
                        arrayContent.SetValue(CreateObject(element, elementType), i);
                    }
                }
            }
            return arrayContent;
        }

        private Type FindType(string name)
        {
            Type type = null;

            foreach (Assembly assembly in AppDomain.CurrentDomain.GetAssemblies())
            {
                type = assembly.GetType(name);
                if (type != null)
                {
                    break;
                }
            }
            return type;
        }
    }
}
